小知识·BitTorrent 简介 您所在的位置:网站首页 announce 的意思 小知识·BitTorrent 简介

小知识·BitTorrent 简介

2023-03-17 13:01| 来源: 网络整理| 查看: 265

BitTorrent 简介 从 P2P 说起

经常在网上飙车的老司机应该都知道 BT 下载,但是有时候拿到了种子却下载不动,会不会很抓狂,是不是还觉得是自己网不行,那作为一个合格的老司机,我们需要探究一下下载不动的原因是什么,BT的运作方式是怎样的,如果你也有这样的疑惑,那么,系好安全带,我们一起来了解一下什么是 BT。

2001年4月,程序员布莱姆·科恩设计了一种协议,然后在2001年7月2日,他发布了 BitTorrent 客户端的第一个实现。

BT(BitTorrent)是 P2P 的一种实现,P2P也叫「对等网络」(英语:peer-to-peer, 简称P2P),是一种在对等者(Peer)之间分配任务和工作负载的分布式应用架构,是对等计算模型在应用层形成的一种网络形式。在P2P网络环境中,彼此连接的多台计算机之间都处于对等的地位,各台计算机有相同的功能,无主从之分,每个节点既充当服务器,为其他节点提供服务,也能作为客户端,享用其他节点提供的服务。

P2P有着很广泛的应用,比如 P2P金融(雾),区块链,BT下载等。它的关键字是去中心化,依靠用户群(peers)来互相传输数据,符合这种特征的都可以称之为 P2P。

BitTorrent

大家肯定有在互联网上下载各种资源的经历,比如电影电视剧,我们在网上一搜,就会搜到一些不知名的小网站,网站上通常会提供一个叫做「种子」的东西,我们使用时只需要把种子下载到电脑上,通常是一个后缀为 .torrent 的文件, 然后用迅雷或者其他的下载工具下载。

在实际操作中,如果我们使用迅雷进行下载,有时候会发现种子下不动,有时候发现下的特别慢,有时候还被提示资源敏感,无法下载,还有时候迅雷提示你开会员可以加速(这个时候开一个会员基本就可以满速下载了,因为迅雷已经把资源提前下载到自己服务器了),我们可能产生一些疑惑:

种子是什么?

为什么资源有时候下不动,有时候速度那么慢?

如何才能让我的BT下载速度变快?

为了解决这个疑问,我们需要了解一下 BT 协议,全称是 BitTorrent,这个协议被设计用来实现 P2P(Peer to Peer) 下载。普通的 HTTP/FTP 下载使用 TCP/IP 协议,BitTorrent 协议是架构于 TCP/IP 协议之上的一个P2P文件传输通信协议,是一个应用层协议。

传统的下载是客户端请求服务器获取资源,下载方和资源提供方的角色很清楚。这样做的优点是简单,易于理解,我要下载东西,我就去请求服务器,缺点也很明显:

一旦服务器故障,大家都无法下载

服务器带宽有限,下载的人多速度必然下降

而 P2P 则不一样,每一个客户端同时也是服务器,从别人那里下载资源的同时,也提供资源给到别人。这样一来,就规避了服务器模型的缺点:

每个人都是服务器,除非所有机器都故障了,否则网络依旧可以运转

不会去请求单一机器,而是从多个机器获取资源,这样可以使带宽得到最大利用

种子的格式与作用

我们对 BT 的认知,一般是从种子开始的,所以首先需要了解一下种子的格式与作用。

我们一般下载下来的文件是一个以 .torrent 结尾的文件,通过文本编辑器打开,会看见一堆乱码,它并不是一个纯文本文件,而是一个二进制文件,通过查资料,可以发现种子文件中采用了一种文件编码,叫做 Bencode ,这种编码以 ACSII 字符来进行编码,里面包含几种简单的数据结构,我们一起来了解一下:

Bencode 编码 字符串

将一个字符串的前面加上长度标识和符号(冒号),这就是 Bencode 编码后的字符串了,比如:

'hello' -> 5:hello 'How are you' -> 11:How are you 整数

一个整数起始以 i 作为标识,结尾以 e 来作为标识,把数字写在中间即可,如:

123 -> i123e 666 -> i666e 0 -> i0e 列表

列表可以类比为 Python 中的列表,是一种容器性质的数据结构,每个元素可以是四种数据结构中的任意一组,没有长度限制。语法是,列表的开头和结尾分别用 l 和 e 作为标识符,中间的值就是任意的数据结构。

[123,666,0] -> li123ei666ei0ee [123,'hello',456] -> li123e5:helloi456ee 字典

字典的开头和结尾以 d 和 e 作为标识符,bencode中的字典,key 要求必须是字符串格式的,value 的格式可以随便。另外,编码过程,key 要根据字符串的字典序进行升序排序。比如:

{'a':1,'cd':[3,4],'b':2} -> d1:ai1e1:bi2e2:cdli3ei4eee .torrent 种子的格式

关于种子文件的定义,在官方文档:bep_0003.rst_post 里面说的很清楚。实质上,种子文件就是一个使用 Bencode 格式编码的一个 Dictionary,里面含有一些字段,声明了关于这个种子的一些信息。

大家可以把一个种子文件理解成为一个大 Json,只不过是因为压缩需要用二进制的形式存起来了而已。

我写了一个解析器,可以把不可读的 bencode 变成可读的 json 格式:

https://github.com/riba2534/bencode

我们以 Ubuntu20.04.2 官方提供的种子为例:https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/20.04.2.0/ubuntu-20.04.2.0-desktop-amd64.iso.torrent

把这个种子解析之后,会得到一个json文件,如下:

{"announce":"https://torrent.ubuntu.com/announce","announce-list":[["https://torrent.ubuntu.com/announce"],["https://ipv6.torrent.ubuntu.com/announce"]],"comment":"Ubuntu CD releases.ubuntu.com","created by":"mktorrent 1.1","creation date":"2021-02-12 03:02:32","info_hash":"4ba4fbf7231a3a660e86892707d25c135533a16a","info":{"length":2877227008,"name":"ubuntu-20.04.2.0-desktop-amd64.iso","piece length":262144,"pieces":["d89b853053ac28e09d6d322658636d9663aa80fe","287528aae8bda9ef962918ba8db2ceb0638454e4","149987b3a98147d9b5cc1e249b2fea7dc3401eb1","539f5c519a5fcb058d5978b415188340f57039df","c5ac6a46748abef691e96f7913c60c22990d5123","e87e684ca1c31cc029560514058c75c306a6b41c","c19e41f1c980b91ff735af99a2c4ab4d90946344","4707444be592ae107ddd614a3ef79fbc21e090a3","3acce815ec86a6d5bc0677874ab98dba424ddf35","d4e0d04c15514509c14fa97b1eb09f3bdbaff144","f03a8f9c698568221b4582995716b1123b7e7390","3efe825e140ab8137525f2ecaa0b32d46ec62851","数量太多,这里截断,一共10976行 ......."]}}

我们可以发现,种子包含以下几个key:

BT 下载流程概述

刚才说了种子文件的格式,大家可能有疑惑,种子中这么多字段有啥用?我们先来简要了解一下BT下载的完整流程,再详细展开:

这里有一个网站,可以生动形象的展示 BT 下载的流程。 http://mg8.org/processing/bt.html

我们在这里用文字简述一下整个流程。

种子发布者制作种子,且向 Tracker 服务器表明,大家要下载这个种子就来找我。(Tracker 的地址就是种子文件中 announce 字段中的 url)

种子发布者把做好的种子分享到互联网。

下载者在互联网上获取到种子文件

下载者本地的 BT 客户端解析种子文件,拿到 Tarcker 地址,向 Tarcker 发起请求(HTTP或UDP),获取其他 Peer 的地址

Tracker接收到请求后,去自己的存储里找拥有这个种子中的文件的 peers 的 IP:port,返回给下载者,并且把当前下载者的 IP:Port 加入服务器的存储。

下载者与其他 Peer 建立连接,由于一个文件被分成了若干个文件块,所以下载者可以和多个 Peer 下载不同的块,下载完成后,校验块的哈希值,保存在本地。(这也是下载种子的人越多,下载速度越快的原因)

整个文件下载完成时,校验整个文件哈希值,不出意外,下载成功

BT客户端不要关闭,自己作为 Peer 服务 BT 网络中的其他人

BT下载核心思想:人人为我,我为人人

知道了基本下载流程之后,我们继续来了解一下细节。

与 Tracker 进行交互 Tracker 的作用

种子文件中的 announce 字段中包含了一个 url ,这个 url 也就是 tracker 服务器。首先我们来了解 Tracker 是什么,服务器的作用是作为 peers 沟通的桥梁而存在,当下载者要下载某一个资源的时候,就会去向服务器询问,服务器查询之后如果发现自己保存了这个资源的其他节点,就把这些节点的地址返回,然后客户端知道这些 IP:Port 后,就去与其他 Peer 建立连接。

Tracker 不存储任何具体资源的文件信息,只存储文件的哈希值,来帮助 Peers 来建立连接

发布者做种(Seed) 做种:指上传文件数据给其他 BT 用户的行为。

种子只有先被制作发布,才能使用,我们来探究一下种子的发布方法。我们先来实操一下,我现在有一个文件夹,叫做「学习资源」

在 Utorrent 客户端中,我选择添加新的 torrent 文件,首先会让我选择一个目录,然后选一个区块大小(一般是 256KB)用于分割文件为若干个块,点击创建后,会得到一个文件,我解析字段后,如下图所示:

{"announce":"udp://tracker.openbittorrent.com:80/announce","announce-list":[["udp://tracker.openbittorrent.com:80/announce"],["udp://tracker.opentrackr.org:1337/announce"]],"created by":"uTorrent/3.5.5","creation date":"2021-03-27 16:34:08","encoding":"UTF-8","info_hash":"808eb761570975c41a7236ce8feaea5eb3c4c76b","info":{"files":[{"length":6,"path":["1.txt"]},{"length":6,"path":["2.txt"]},{"length":6,"path":["3.txt"]}],"name":"学习资源","piece length":16384,"pieces":["f118f355485f17f340330dc1bafb2f98fca7a455"]}}

一般来说,使用 BT 下载软件进行做种的时候,下载软件会内置几个 tracker 服务器,当然也可以自己找一些 tarcker 的地址添加进去,BT客户端会向这些 tracker 发起请求。Tracker 服务器就会记录下来上传者的 IP:Port ,以便于传输给后续下载者进行下载。

那如果下载者要下载的时候,所有拥有这些文件的人都不在线怎么办。那就真没办法了,这种种子也叫「死种」,因为没人上传,这也就是网上很多种子下载不动的原因。 下载者下载

获取 Peers

作为一个下载者,在开始下载资源之前首先要向 tracker 宣布自己的存在,同时获得其他人的地址。 因此接下来要做的事情就是与tracker通信,获取peers。

我们仍然可以从官方文档中找到下载者与 tarcker 通信的方式 http://www.bittorrent.org/beps/bep_0003.html ,可以看出下载者与 Trakcer 的通信方式有 UDP 和 HTTP 两种协议,具体使用哪一种,看种子里面包含的信息是 udp 还是 http,下面我们以 HTTP 的方式来进行探究:

下载者向 Tracker 发起一个 GET 请求,请求的格式包含的关键字段:

Tracker 接收到 GET 请求后,Tracker服务器就会反连(NatCheck)下载者的IP地址和端口,这样就可以区分内网用户还是公网用户(如果是内网用户,它是连不通的,因为它会连到Nat服务器或者路由器上,结果就是连不通),然后服务器返回现在正在下载这个文件的所有公网用户的IP地址和端口列表,返回给BT客户端(也可能是部分客户列表),最后如果该用户是公网用户 Tracker服务器会把用户提交的IP地址和端口保存下来,这样其他人就可以找到该用户。

在返回的 Body 中,也是一个用 Bencode 编码的信息,正常响应至少要包含 interval 和 peers 两个字段。其中,interval 用于告诉客户端间隔多久再向服务端发一次请求(当然客户端有可能完全不理会),peers 字段包含同伴的 peer_id、ip、port 等信息。

BT客户端得到这些其他用户IP后,就可以直接连接到这些IP和端口下载资料了。BT客户端会到所有的用户去寻找自己要下载的东西。BT客户端每找到一个用户就建立一个 Socket来下载 ,所以下载的人越多,速度就越快。

我们可以来看一下整个下载过程,这里我参考了一个开源的 torrent 客户端的实现: https://github.com/veggiedefender/torrent-client

一个种子文件中的 info.pieces 字段包含了每个小块的哈希值,而每个块的大小都是 256KB。这样就相当于把下载任务分解了,分解成了下载若干个大小为 256KB 的小块的任务。采用并发的方式去依次请求每个块的数据,然后计算好数据的位置放进最终结果中返回,然后把内存中的数据写入硬盘。

// Download downloads the torrent. This stores the entire file in memory. func(t*Torrent)Download()([]byte,error){log.Println("Starting download for",t.Name)// Init queues for workers to retrieve work and send results workQueue:=make(chan*pieceWork,len(t.PieceHashes))// 任务队列,包含了若干个下载任务 results:=make(chan*pieceResult)// 结果队列,当对应的块下载完成后,就放进结果队列中 forindex,hash:=ranget.PieceHashes{length:=t.calculatePieceSize(index)// 计算每一个序号所对应的块的长度 workQueue


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有